OAuth 2.0 voor client-side web applicaties
Home

OAuth 2.0 voor client-side web applicaties

OAuth 2.0 voor client-side web applicaties

De Google OAuth 2.0 endpoint JavaScript-centric toepassingen. Deze toepassingen kunnen toegang krijgen tot een Google API terwijl de gebruiker de broncode van de toepassing kan zien.Dit type van toepassing kan dus geen geheim bewaren. Deze autorisatie flow staat bekend als de impliciete implicit grant flow.

In dit artikel leer je hoe je OAuth 2.0 gebruikt om een Google API aan te spreken vanuit een JavaScript-applicatie.

Inleiding

Google definiëert twee niveau's van of API toegang:

Level Beschrijving Vereist:
eenvoudig API calls hebben geen toegang tot privégegevens van de gebruiker API key
geverifieerd API calls kunnen priveégegevens van de gebruiker lezen en wijzigen, evenals de gegevens van de applicatie zelf OAuth 2.0 credential

Hoe een applicatie een API verzoek verstuurt met behulp van de JavaScript-client library

Er zijn vele manieren om de JavaScript-client library gebruiken om API-verzoeken te maken. Dat stel je al gauw genoeg vast als je wat rondneust op het internet. Het is belangrijk het basispatroon erin te herkennen:

  1. De applicatie laadt de JavaScript client library.
  2. De applicatie stelt haar API-sleutel in, waarmee de applicatie geverifiëerd word door de Google-services.
  3. Als de toepassing toegang tot persoonlijke gegevens van de gebruiker nodig heeft, wordt haar OAuth 2.0 client-ID ingesteld en wordt een sessie geopend met een Google-authenticatie server. De authenticatie server opent een dialoogvenster waarin de gebruiker wordt gevraagd om het gebruik van persoonlijke gegevens toe te staan.
  4. De applicatie laadt de API voor de Google-service.
  5. De toepassing initialiseert een request object (ook wel een service object genoemd) die bepaalt welke gegevens worden geretourneerd door de API.
  6. De applicatie voert het verzoek uit en verwerkt de door de API geretourneerde gegevens.

Scenario

Ons uiteindelijke doel is een app maken waarmee een leerkracht lesmateriaal kan maken. We werknaam van het project is myaa wat staat voor My Authoring App.

We willen het Google-profiel van de gebruiker opvragen.

Stappen

Een API key aanmaken

Hoe je een API key maakt lees je op de pagina: De ontwikkelaarsconsole. Dit hoef je maar één keer te doen.

Om de oefeningen te maken is dat zelfs niet nodig. De cursisten kunnen mijn Myaa API key gebruiken. Maar dan moet de cursist wel een mail naar mij sturen met de CLOUD9 url die de login aanvraagt.Ik moet die toevoegen aan de Key restriction op het API key tabblad.

Een client_ID aanmaken

Hoe je een client_ID aanmaakt lees je op volgende pagina: De ontwikkelaarsconsole. Dit hoef je maar één keer te doen.

Om de oefeningen te maken is dat zelfs niet nodig. De cursisten kunnen mijn Myaa API key gebruiken. Maar dan moet de cursist wel een mail naar mij sturen met de CLOUD9 url die de login aanvraagt.Ik moet die toevoegen aan de Restrictions op de OAuth consent screen.

Ik deel die key's met jullie, maar ik beveilig het gebruik van de key's door beperkingen op te leggen: alleen de clients die in de restrictions zijn opgenomen hebben toegang tot de app.

De HTML

<body>
    <h1>De People Google API gebruiken</h1>
    <!--Knoppen toevoegen om auth te initialiseren en af te melden -->
    <button id="authorize-button" style="display: none;">Aanmelden</button>
    <button id="signout-button" style="display: none;">Afmelden</button>
    <div id="content"></div>
    <pre id="response"></pre>
</body>

Definieer variabelen

We hebben drie variabelen nodig waarin we de clientId, de apiKey en de scopes opslaan:

var clientId = 'YOUR CLIENT ID';
var apiKey = 'YOUR API KEY';
var scopes = 'profile';

Ingevuld met de key's voor myaa:

var apiKey = 'AIzaSyAmd2VnWexHajlyARnDykoXQUHyGg24RsM';
var clientId = '420594077145-po45ldmitjnulihvt5309kmeoltb9pmq.apps.googleusercontent.com';
var scopes = 'profile';

We beginnen met de profile scope en kijken wat we daarmee kunnen doen.

De Google API laden

Let goed op waar op je HTML pagina dat je die code plaats. Ik plaats die onder mijn de body tag omdat de functies die ik ga definiëren de HTML nodig hebben en dus alle HTML elementen al in de pagina moeten zijn geladen (zie daarvoor JS - plaats in HTML)

<script src="https://apis.google.com/js/api.js?onload=handleClientLoad"></script>

De querystring bevat een onload parameter met de waarde handleClientLoad. We beginnen met die methode aan te maken:

function handleClientLoad() {
        // Load the API client and auth library
        gapi.load('client:auth2', initAuth);
}

Aan- en afmeldingsmethoden

In de initAuth methode, die wordt opgeroepen door de handleClientLoad methode, initialiseren we het gapi.auth2 object met onze scope en onze clientId. In de then methode van de Promise roepen we een functie op die kijkt of de gebruiker als is aangemeld en op basis daarvan initialiseren we onze knoppen:

function initAuth() {
  // gapi.client.setApiKey(apiKey);
  gapi.auth2.init({
          'apiKey': apiKey,
          client_id: clientId,
          scope: scopes
      }).then(function() {
          // Listen for sign-in state changes.
          gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
          // Handle the initial sign-in state.
          updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
          signinButton.addEventListener("click", signin);
          signoutButton.addEventListener("click", signout);
      }).then(makePeopleApiCall)
      .then(showUserProfile)
      .catch(function(e) {
          alert(e.details);
      });
}

De methode updateSigninStatus initialiseert de knoppen op de HTML pagina. Als de gebruiker reeds aangemeld is wordt de Afmelden knop getoond. Is de gebruiker nog niet aangemeld wordt de Aanmelden knop getoond.

function updateSigninStatus(isSignedIn) {
  if (isSignedIn) {
    signinButton.style.display = 'none';
    signoutButton.style.display = 'block';
    // makeApiCall();
  } else {
    signinButton.style.display = 'block';
    signoutButton.style.display = 'none';
  }
}

De signin methode opent het dialoogvenster waarmee een gebuiker een account kan selecteren en zich kan aanmelden en de signout methode meld de gebruiker af.

function signin(event) {
  gapi.auth2.getAuthInstance().signIn();
}

function signout(event) {
  gapi.auth2.getAuthInstance().signOut();
}

De volledige code

Deze code gaan we heel vaak nodig hebben. We gaan nu een eerste stap zetten om de code zodanig te structuren dat we deze gemakkelijk kunnen herbegruiken.

HTML

Op de webpagina hebben we steeds de twee koppen Aan- en Afmelden nodig:

<!doctype html>
<html lang="nl">
<head>
    <meta charset="UTF-8">
    <title>Google oAuth - Een eerste begin</title>

 </head>

<body>
    <h1>De People Google API gebruiken</h1>
    <!--Knoppen toevoegen om auth te initialiseren en af te melden -->
    <button id="signin-button" style="display: none;">Aanmelden</button>
    <button id="signout-button" style="display: none;">Afmelden</button>
    <div id="content"></div>
    <pre id="response"></pre>
    <script>
    </script>
    <script type="text/javascript" src="js/google-oauth-api.js"></script>
    <script src="https://apis.google.com/js/api.js?onload=handleClientLoad"></script>
</body>
</html>

JS

Het google-oauth-api.js bestand

We plaatsen alle JavaScript in een apart bestand met de naam google-oauth-api.js in de js map:

// Enter an API key from the Google API Console:
//   https://console.developers.google.com/apis/credentials?project=_
// Enter an API key from the Google API Console:
//   https://console.developers.google.com/apis/credentials?project=_
var apiKey = 'AIzaSyDx-m6cpF2b-aDz_D_DqS9-Z3KbHGEUIf8';

// Enter a client ID for a web application from the Google API Console:
//   https://console.developers.google.com/apis/credentials?project=_
// In your API Console project, add a JavaScript origin that corresponds
//   to the domain where you will be running the script.
var clientId = '567196011386-np3nt5nng9oeokfbk8medpf42u2iun6c.apps.googleusercontent.com';
// Enter one or more authorization scopes. Refer to the documentation for
// the API or https://developers.google.com/identity/protocols/googlescopes
// for details.
var scopes = 'profile';

var signinButton = document.getElementById('signin-button');
var signoutButton = document.getElementById('signout-button');

function handleClientLoad() {
    // Load the API client and auth library
    gapi.load('client:auth2', initAuth);
}

function initAuth() {
    // gapi.client.setApiKey(apiKey);
    gapi.auth2.init({
            'apiKey': apiKey,
            client_id: clientId,
            scope: scopes
        }).then(function() {
            // Listen for sign-in state changes.
            gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
            // Handle the initial sign-in state.
            updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
            signinButton.addEventListener("click", signin);
            signoutButton.addEventListener("click", signout);
        }).then(makePeopleApiCall)
        .then(showUserProfile)
        .catch(function(e) {
            alert(e.details);
        });
}

function updateSigninStatus(isSignedIn) {
    if (isSignedIn) {
        signinButton.style.display = 'none';
        signoutButton.style.display = 'block';
        // makeApiCall();
    }
    else {
        signinButton.style.display = 'block';
        signoutButton.style.display = 'none';
    }
}

function signin(event) {
    gapi.auth2.getAuthInstance().signIn();
}

function signout(event) {
    gapi.auth2.getAuthInstance().signOut();
}

// Load the API and make an API call.  Display the results on the screen.
function makePeopleApiCall() {
    gapi.client.load('people', 'v1', function() {
        var request = gapi.client.people.people.get({
            resourceName: 'people/me',
            'requestMask.includeField': 'person.names'
        });
        request.execute(function(resp) {
            var p = document.createElement('p');
            if (resp.names) {
                var name = resp.names[0].givenName;
            }
            else {
                var name = 'Geen naam gevonden';
            }
            p.appendChild(document.createTextNode('Hello, ' + name + '!'));
            document.getElementById('content').appendChild(p);
            // Toon het response object als JSON string
            var pre = document.createElement('pre');
            var feedback = JSON.stringify(resp, null, 4);
            pre.appendChild(document.createTextNode(feedback));
            document.getElementById('content').appendChild(pre);
        });
    });
}

function showUserProfile(resp) {
    // Note: In this example, we use the People API to get the current
    // user's name. In a real app, you would likely get basic profile info
    // from the GoogleUser object to avoid the extra network round trip.
    var profile = gapi.auth2.getAuthInstance().currentUser.get().getBasicProfile();
    var h1 = document.createElement('h1');
    h1.appendChild(document.createTextNode(profile.getId()));
    document.getElementById('content').appendChild(h1);
    var h2 = document.createElement('h2');
    h2.appendChild(document.createTextNode(profile.getName()));
    document.getElementById('content').appendChild(h2);
    var h3 = document.createElement('h3');
    h3.appendChild(document.createTextNode(profile.getGivenName()));
    document.getElementById('content').appendChild(h3);
    var h4 = document.createElement('h4');
    h4.appendChild(document.createTextNode(profile.getFamilyName()));
    document.getElementById('content').appendChild(h4);
    var img = document.createElement('img');
    img.setAttribute("src", profile.getImageUrl());
    document.getElementById('content').appendChild(img);
    var h5 = document.createElement('h5');
    h5.appendChild(document.createTextNode(profile.getEmail()));
    document.getElementById('content').appendChild(h5);
}

Het helpers.js bestand

Nog een handigheid is de helper functie showFeedback. Met deze methode kunnen we gemakkelijk de response van een API call laten zien. Dat is handig zolang we aan ontwikkelen zijn. Deze methode staat in een bestand met de naam helpers.js in de js map:

/**
 * Helper function showFeedback
 * Append a p element to the feedback html element
 * with the given text as its text node.
 *
 * @param {string} text Text to be placed in the p element.
 */
function showFeedback(text) {
    var feedback = document.getElementById('feedback');
    if (!feedback) {
        feedback = document.createElement('div');
        document.body.appendChild(feedback);
    }
    var p = document.createElement('p');
    var textContent = document.createTextNode(text);
    p.appendChild(textContent);
    feedback.appendChild(p);
}

Bronnen

Bekijk het volgende filmpje van Google om een overzicht te krijgen van OAuth. Het Engels is niet perfect maar toch is het een beknopte en goede inleiding.

De Google documentatie vind je op Authentication using the Google APIs Client Library for JavaScript.

Een zeer goede tutorial vind je op Tamás Sallai, Using Google auth in Javascript, 12 mei 2015

JI
2018-04-18 20:58:57